{% extends "tutorial.html" %}

{% block head %}
<style>
{% if is_mobile %}
.example {
  padding: 10px;
  border: 1px solid #CCC;
}
#example-list-fs ul {
  padding-left: 0;
}
#example-list-fs li {
  list-style: none;
}
#example-list-fs img {
  vertical-align: middle;
}
{% endif %}
</style>
{% endblock %}

{% block onload %}{% endblock %}

{% block html5badge %}
<img src="/static/images/identity/html5-badge-h-storage.png" width="133" height="64" alt="This article is powered by HTML5 Offline &amp; Storage" title="This article is powered by HTML5 Offline &amp; Storage" />
{% endblock %}

{% block iscompatible %}
  return !!(window.requestFileSystem || window.webkitRequestFileSystem);
{% endblock %}

{% block content %}
  <p class="notice warning" style="text-align:center">
  In April 2014, it was <a href="http://lists.w3.org/Archives/Public/public-webapps/2014AprJun/0010.html">announced on public-webapps</a> 
  that the Filesystem API spec is not being considered by other browsers. For now, the API is Chrome-specific and it's unlikely to be implemented by other browsers and is no longer being standardized with the W3C.</p>

  <h2 id="toc-introduction">Introduction</h2>
  <p>I've always thought it would be handy if web applications could read and
  write files and directories. As we move from offline to online, applications are becoming
  more complex and the lack of file system APIs has been a hindrance for moving the web
  forward. Storing or interacting with binary data shouldn't be limited to the
  desktop. Thankfully, it no longer is thanks to the
  <a href="http://dev.w3.org/2009/dap/file-system/pub/FileSystem/">FileSystem API</a>.
  With the FileSystem API, a web app can create, read, navigate, and write to a
  sandboxed section of the user's local file system.</p>

  <p>The API is broken up into various themes:</p>
  <ul>
    <li>Reading and manipulating files:
      <code>File</code>/<code>Blob</code>, <code>FileList</code>, <code>FileReader</code></li>
    <li>Creating and writing:
      <code>Blob()</code>, <code>FileWriter</code></li>
    <li>Directories and file system access:
      <code>DirectoryReader</code>, <code>FileEntry</code>/<code>DirectoryEntry</code>, <code>LocalFileSystem</code>
    </li>
  </ul>

  <h3 id="toc-support">Browser support &amp; storage limitations</h3>

  <p>At the time of writing this article, Google Chrome has the only working implementation of the
  FileSystem API. A dedicated browser UI does not yet exist for file/quota
  management. To store data on the user's system, may require your app to <a href="#toc-requesting-quota">request quota</a>.
  However, for testing, Chrome can be run with the <code>--unlimited-quota-for-files</code> flag.
  Furthermore, if you're building an app or extension for the Chrome Web Store, the <code>unlimitedStorage</code> <a href="http://code.google.com/chrome/extensions/manifest.html">manifest file</a>
  permission can be used in place of requesting quota. Eventually, users will receive a permission
  dialog to grant, deny, or increase storage for an app.</p>

  <p>You may need the <code>--allow-file-access-from-files</code> flag if you're debugging
  your app from <code>file://</code>. Not using these flags will result in
  a <code>SECURITY_ERR</code> or <code>QUOTA_EXCEEDED_ERR</code> FileError.</p>

  <h2 id="toc-requesting">Requesting a file system</h2>

  <p>A web app can request access to a sandboxed file system by calling
  <code>window.requestFileSystem()</code>:</p>

  <pre class="prettyprint">
// Note: The file system has been prefixed as of Google Chrome 12:
window.requestFileSystem  = window.requestFileSystem || window.webkitRequestFileSystem;

window.requestFileSystem(<var>type</var>, <var>size</var>, <var>successCallback</var>, <var>opt_errorCallback</var>)
</pre>

  <dl>
    <dt><var>type</var></dt>
    <dd>Whether the file storage should be persistent.
    Possible values are <code>window.TEMPORARY</code> or <code>window.PERSISTENT</code>.
    Data stored using <code>TEMPORARY</code> can be removed at the browser's discretion
    (for example if more space is needed). <code>PERSISTENT</code> storage cannot be cleared
    unless explicitly authorized by the user or the app and requires the user to grant
    quota to your app. See <a href="#toc-requesting-quota">requesting quota</a>.</dd>
    <dt><var>size</var></dt>
    <dd>Size (in bytes) the app will require for storage.</dd>
    <dt><var>successCallback</var></dt>
    <dd>Callback that is invoked on successful request of a file system. Its argument
    is a <a href="http://dev.w3.org/2009/dap/file-system/pub/FileSystem/#idl-def-FileSystem"><code>FileSystem</code></a> object.</dd>
    <dt><var>opt_errorCallback</var></dt>
    <dd>Optional callback for handling errors or when the request to obtain
    the file system is denied. Its argument is a
    <a href="http://dev.w3.org/2009/dap/file-system/pub/FileSystem/#idl-def-FileError"><code>FileError</code></a> object.</dd>
  </dl>

  <p>If you're calling <code>requestFileSystem()</code> for the first time, new storage is
  created for your app. It's important to remember that this file system is
  sandboxed, meaning one web app cannot access another app's files. This also
  means you cannot read/write files to an arbitrary folder on the user's hard drive
  (for example My Pictures, My Documents, etc.).</p>

  <p>Example usage:</p>
  <pre class="prettyprint">
function onInitFs(fs) {
  console.log('Opened file system: ' + fs.name);
}

<b>window.requestFileSystem</b>(<b>window.TEMPORARY</b>, 5*1024*1024 /*5MB*/, onInitFs, errorHandler);
</pre>

  <p>The FileSystem specification also defines a synchronous API, <code><a href="http://dev.w3.org/2009/dap/file-system/file-dir-sys.html#using-localfilesystemsync">LocalFileSystemSync</a></code>
  interface that is intended to be used in <a href="/tutorials/#workers">Web Workers</a>.
  However, this tutorial will not cover the synchronous API.</p>

  <p>Throughout the remainder of this document, we'll use the same handler for
  processing errors from the asynchronous calls:</p>

  <pre class="prettyprint">
function errorHandler(e) {
  var msg = '';

  switch (e.code) {
    case <b>FileError.QUOTA_EXCEEDED_ERR</b>:
      msg = 'QUOTA_EXCEEDED_ERR';
      break;
    case <b>FileError.NOT_FOUND_ERR</b>:
      msg = 'NOT_FOUND_ERR';
      break;
    case <b>FileError.SECURITY_ERR</b>:
      msg = 'SECURITY_ERR';
      break;
    case <b>FileError.INVALID_MODIFICATION_ERR</b>:
      msg = 'INVALID_MODIFICATION_ERR';
      break;
    case <b>FileError.INVALID_STATE_ERR</b>:
      msg = 'INVALID_STATE_ERR';
      break;
    default:
      msg = 'Unknown Error';
      break;
  };

  console.log('Error: ' + msg);
}
</pre>

  <p>Granted, this error callback is very generic, but you get the idea. You'll want to
  provide human-readable messages to users instead.</p>

  <h3 id="toc-requesting-quota">Requesting storage quota</h3>

  <p>To use <code>PERSISTENT</code> storage, you must obtain permission from the user
  to store peristent data. The same restriction doesn't apply to <code>TEMPORARY</code> storage
  because the browser may choose to evict temporarily stored data at its discretion.</p>

  <p>To use <code>PERSISTENT</code> storage with the FileSystem API, Chrome exposes a new
  API under <code>window.webkitStorageInfo</code> to request storage:</p>

  <pre class="prettyprint">
window.webkitStorageInfo.requestQuota(PERSISTENT, 1024*1024, function(grantedBytes) {
  window.requestFileSystem(PERSISTENT, grantedBytes, onInitFs, errorHandler);
}, function(e) {
  console.log('Error', e);
});
</pre>

  <p>Once the user has granted permission, there's no need to call <code>requestQuota()</code>
  in the future (unless you wish to increase your app's quota).
  Subsequent calls for equal or lesser quota are a noop.</p>

  <p>There is also <a href="https://groups.google.com/a/chromium.org/group/chromium-html5/browse_thread/thread/9be7a2dc04d9af67">an API</a> to query an origin's current quota
  usage and allocation: <code>window.webkitStorageInfo.queryUsageAndQuota()</code></p>

  <h2 id="toc-file">Working with files</h2>

  <p>Files in the sandboxed environment are represented by the
  <a href="http://dev.w3.org/2009/dap/file-system/pub/FileSystem/#the-fileentry-interface"><code>FileEntry</code></a>
  interface. A FileEntry contains the types of properties (<code>name</code>, <code>isFile</code>, ...)
  and methods (<code>remove</code>, <code>moveTo</code>, <code>copyTo</code>, ...) that
  you'd expect from a standard file system.</p>

  <p id="toc-fileentry">Properties and methods of <code>FileEntry</code>:</p>
  <pre class="prettyprint">
fileEntry.isFile === true
fileEntry.isDirectory === false
fileEntry.name
fileEntry.fullPath
...

fileEntry.getMetadata(successCallback, opt_errorCallback);
fileEntry.remove(successCallback, opt_errorCallback);
fileEntry.moveTo(dirEntry, opt_newName, opt_successCallback, opt_errorCallback);
fileEntry.copyTo(dirEntry, opt_newName, opt_successCallback, opt_errorCallback);
fileEntry.getParent(successCallback, opt_errorCallback);
fileEntry.toURL(opt_mimeType);

fileEntry.file(successCallback, opt_errorCallback);
fileEntry.createWriter(successCallback, opt_errorCallback);
...
</pre>

  <p>To better understand <code>FileEntry</code>, the rest of this section contains
  a bunch of recipes for performing common tasks.

  <h3 id="toc-file-creatingempty">Creating a file</h3>

  <p>You can look up or create a file with the file system's <code>getFile()</code> method,
  a method of the <a href="#toc-direntry">DirectoryEntry</a> interface.
  After requesting a file system, the success callback is passed a <code>FileSystem</code>
  object that contains a <code>DirectoryEntry</code> (<code>fs.root</code>) pointing to
  the root of the app's file system.</p>

  <p>The following code creates an empty file called "log.txt" in the root of the app's file system:</p>

  <pre class="prettyprint">
function onInitFs(fs) {

  <b>fs.root.getFile</b>('log.txt', <b>{create: true, exclusive: true}</b>, function(fileEntry) {

    // fileEntry.isFile === true
    // fileEntry.name == 'log.txt'
    // fileEntry.fullPath == '/log.txt'

  }, errorHandler);

}

window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
</pre>

  <p>Once the file system has been requested, the success handler is passed
  a <code>FileSystem</code> object. Inside the callback, we can call <code>fs.root.getFile()</code>
  with the name of the file to create. You can pass an absolute or relative path, but
  it must be valid. For instance, it is an error to attempt to create a file
  whose immediate parent does not exist. The second argument to <code>getFile()</code>
  is an object literal describing the function's behavior if the file does not exist. In this
  example, <code>create: true</code> creates the file if it doesn't exist
  and throws an error if it does (<code>exclusive: true</code>). Otherwise (if
  <code>create: false</code>), the file is simply fetched and returned.
  In either case, the file contents are not overwritten because we're just obtaining a
  reference entry to the file in question.</p>

  <h3 id="toc-file-readingbyname">Reading a file by name</h3>

  <p>The following code retrieves the file called "log.txt",
  its contents are read using the <code>FileReader</code> API and appended to a new
  &lt;textarea&gt; on the page. If log.txt doesn't exist,
  an error is thrown.</p>

  <pre class="prettyprint">
function onInitFs(fs) {

  <b>fs.root.getFile</b>('log.txt', {}, function(fileEntry) {

    // Get a File object representing the file,
    // then use FileReader to read its contents.
    fileEntry.<b>file</b>(function(file) {
       var reader = new FileReader();

       reader.onloadend = function(e) {
         var txtArea = document.createElement('textarea');
         txtArea.value = this.result;
         document.body.appendChild(txtArea);
       };

       reader.readAsText(file);
    }, errorHandler);

  }, errorHandler);

}

window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
</pre>

  <h3 id="toc-file-writing">Writing to a file</h3>

  <p>The following code creates an empty file called "log.txt" (if it doesn't exist)
  and fills it with the text 'Lorem Ipsum'.</p>

  <pre class="prettyprint">
function onInitFs(fs) {

  <b>fs.root.getFile</b>('log.txt', <b>{create: true}</b>, function(fileEntry) {

    // Create a FileWriter object for our FileEntry (log.txt).
    <b>fileEntry.createWriter</b>(function(fileWriter) {

      <b>fileWriter.onwriteend</b> = function(e) {
        console.log('Write completed.');
      };

      <b>fileWriter.onerror</b> = function(e) {
        console.log('Write failed: ' + e.toString());
      };

      // Create a new Blob and write it to log.txt.
      var blob = new Blob(['Lorem Ipsum'], {type: 'text/plain'});

      <b>fileWriter.write</b>(blob);

    }, errorHandler);

  }, errorHandler);

}

window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
</pre>

  <p>This time, we call the FileEntry's <code>createWriter()</code> method to
  obtain a <code>FileWriter</code> object. Inside the success callback, event
  handlers are set up for <code>error</code> and <code>writeend</code> events.
  The text data is written to the file by creating a blob, appending text to it,
  and passing the blob to <code>FileWriter.write()</code>.</p>

  <h3 id="toc-file-appending">Appending data to a file</h3>

  <p>The following code appends the text 'Hello World' to the end of our log file.
  An error is thrown if the file does not exist.</p>

  <pre class="prettyprint">
function onInitFs(fs) {

  <b>fs.root.getFile</b>('log.txt', <b>{create: false}</b>, function(fileEntry) {

    // Create a FileWriter object for our FileEntry (log.txt).
    fileEntry.createWriter(function(fileWriter) {

      <b>fileWriter.seek(fileWriter.length); // Start write position at EOF.</b>

      // Create a new Blob and write it to log.txt.
      var blob = new Blob(['Hello World'], {type: 'text/plain'});

      <b>fileWriter.write</b>(blob);

    }, errorHandler);

  }, errorHandler);

}

window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
</pre>

  <h3 id="toc-file-user-selected">Duplicating user-selected files</h3>

  <p>The following code allows a user to select multiple files using
  <code>&lt;input type="file" multiple /&gt;</code> and creates copies of those
  files in the app's sandboxed file system.</p>

  <pre class="prettyprint">&lt;input type="file" id="myfile" <b>multiple</b> /&gt;</pre>

  <pre class="prettyprint">
document.querySelector('#myfile').onchange = function(e) {
  var files = <b>this.files</b>;

  window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
    // Duplicate each file the user selected to the app's fs.
    for (var i = 0, file; file = files[i]; ++i) {

      // Capture current iteration's file in local scope for the getFile() callback.
      (function(f) {
        fs.root.getFile(<b>f.name</b>, {create: true, exclusive: true}, function(fileEntry) {
          fileEntry.createWriter(function(fileWriter) {
            <b>fileWriter.write(f);</b> // Note: write() can take a File or Blob object.
          }, errorHandler);
        }, errorHandler);
      })(file);

    }
  }, errorHandler);

};
</pre>

  <p>Although we've used an input for the file import, one could easily leverage
  <a href="/tutorials/dnd/basics/#toc-desktop-dnd-into">HTML5 Drag and Drop</a>
  to achieve the same objective.</p>

  <p>As noted in the comment, <code>FileWriter.write()</code> can accept a <code>Blob</code>
  or <code>File</code>. This is because <code>File</code> inherits from <code>Blob</code>.
  Therefore, all file objects are blobs.</p>

  <h3 id="toc-file-removing">Removing a file</h3>

  <p>The following code deletes the file 'log.txt'.</p>

  <pre class="prettyprint">
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
  fs.root.getFile('log.txt', {create: false}, function(fileEntry) {

    <b>fileEntry.remove(function() {
      console.log('File removed.');
    }, errorHandler);</b>

  }, errorHandler);
}, errorHandler);
</pre>

  <h2 id="toc-dir">Working with directories</h2>

  <p>Directories in the sandbox are represented by the
  <a href="http://dev.w3.org/2009/dap/file-system/pub/FileSystem/#the-directoryentry-interface"><code>DirectoryEntry</code></a>
  interface, which shares most of <a href="#toc-fileentry">FileEntry</a>'s properties
  (they inherit from a common <code>Entry</code> interface). However, <code>DirectoryEntry</code>
  has additional methods for manipulating directories.</p>

  <p>Properties and methods of <code>DirectoryEntry</code>:</p>
  <pre class="prettyprint">
dirEntry.isDirectory === true
// See the section on <a href="#toc-fileentry">FileEntry</a> for other inherited properties/methods.
...

var dirReader = dirEntry.createReader();
dirEntry.getFile(path, opt_flags, opt_successCallback, opt_errorCallback);
dirEntry.getDirectory(path, opt_flags, opt_successCallback, opt_errorCallback);
dirEntry.removeRecursively(successCallback, opt_errorCallback);
...
</pre>

  <h3 id="toc-dir-creating">Creating directories</h3>

  <p>Use the <code>getDirectory()</code> method of <code>DirectoryEntry</code>
  to read or create directories. You can pass either a name or path as
  the directory to look up or create.</p>

  <p>For example, the following code creates a directory named "MyPictures" in the
  root directory:</p>

  <pre class="prettyprint">
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
  fs.root.<b>getDirectory</b>('MyPictures', {create: true}, function(dirEntry) {
    ...
  }, errorHandler);
}, errorHandler);
  </pre>

  <h3 id="toc-dir-subirs">Subdirectories</h3>

  <p>Creating a subdirectory is exactly the same as creating any other directory. However,
  the API throws an error if you attempt to create a directory whose immediate
  parent does not exist. The solution is to create each directory sequentially, which
  is rather tricky to do with an asynchronous API.</p>

  <p>The following code creates a new hierarchy (music/genres/jazz) in the
  root of the app's FileSystem by recursively adding each subdirectory after its
  parent folder has been created.</p>

  <pre class="prettyprint">
var path = 'music/genres/jazz/';

function createDir(rootDirEntry, folders) {
  // Throw out './' or '/' and move on to prevent something like '/foo/.//bar'.
  if (folders[0] == '.' || folders[0] == '') {
    folders = folders.slice(1);
  }
  rootDirEntry.<b>getDirectory</b>(folders[0], <b>{create: true}</b>, function(dirEntry) {
    // Recursively add the new subfolder (if we still have another to create).
    if (folders.length) {
      createDir(dirEntry, folders.slice(1));
    }
  }, errorHandler);
};

function onInitFs(fs) {
  createDir(fs.root, path.split('/')); // fs.root is a DirectoryEntry.
}

window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
</pre>

  <p>Now that "music/genres/jazz" is in place, we can pass its full path
  to <code>getDirectory()</code> and create new subfolders or files under it.
  For example:</p>

  <pre class="prettyprint">
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
  fs.root.getFile('/music/genres/jazz/song.mp3', {create: true}, function(fileEntry) {
    ...
  }, errorHandler);
}, errorHandler);
</pre>

  <h3 id="toc-dir-reading">Reading a directory's contents</h3>

  <p>To read the contents of a directory, create a <code>DirectoryReader</code>
  and call its <code>readEntries()</code> method. There is no
  guarantee that all of a directory's entries will be returned
  in a single call to <code>readEntries()</code>. That means you need to keep calling
  <code>DirectoryReader.readEntries()</code> until no more results are returned.
  The following is code that demonstrates this:</p>

  <pre class="prettyprint">&lt;ul id="filelist"&gt;&lt;/ul&gt;</pre>

  <pre class="prettyprint">
function toArray(list) {
  return Array.prototype.slice.call(list || [], 0);
}

function listResults(entries) {
  // Document fragments can improve performance since they're only appended
  // to the DOM once. Only one browser reflow occurs.
  var fragment = document.createDocumentFragment();

  entries.forEach(function(entry, i) {
    var img = <b>entry.isDirectory</b> ? '&lt;img src="folder-icon.gif"&gt;' :
                                  '&lt;img src="file-icon.gif"&gt;';
    var li = document.createElement('li');
    li.innerHTML = [img, '&lt;span&gt;', entry.name, '&lt;/span&gt;'].join('');
    fragment.appendChild(li);
  });

  document.querySelector('#filelist').appendChild(fragment);
}

function onInitFs(fs) {

  var dirReader = <b>fs.root.createReader()</b>;
  var entries = [];

  // Call the reader.readEntries() until no more results are returned.
  var readEntries = function() {
     <b>dirReader.readEntries </b>(function(results) {
      if (!results.length) {
        listResults(entries.sort());
      } else {
        entries = entries.concat(toArray(results));
        readEntries();
      }
    }, errorHandler);
  };

  readEntries(); // Start reading dirs.

}

window.requestFileSystem(window.TEMPORARY, 1024*1024, onInitFs, errorHandler);
</pre>

  <h3 id="toc-dir-removing">Removing a directory</h3>

  <p>The <code>DirectoryEntry.remove()</code> method behaves just like
  <a href="#toc-file-removing"><code>FileEntry</code></a>'s. The difference:
  attempting to delete a non-empty directory results in an error.</p>

  <p>The following removes the empty directory "jazz" from "/music/genres/":</p>

  <pre class="prettyprint">
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
  fs.root.getDirectory('music/genres/jazz', {}, function(dirEntry) {

    <b>dirEntry.remove(function() {
      console.log('Directory removed.');
    }, errorHandler);</b>

  }, errorHandler);
}, errorHandler);
</pre>

  <h4 id="toc-dir-rremoving">Recursively removing a directory</h4>

  <p>If you have a pesky directory that contains entries, <code>removeRecursively()</code>
  is your friend. It deletes the directory and its contents, recursively.</p>

  <p>The following code recursively removes the directory "music" and all the
  files and directories that it contains:</p>

  <pre class="prettyprint">
window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
  fs.root.getDirectory('/misc/../music', {}, function(dirEntry) {

    <b>dirEntry.removeRecursively(function() {
      console.log('Directory removed.');
    }, errorHandler);</b>

  }, errorHandler);
}, errorHandler);
</pre>

  <h2 id="toc-copy-rename-move">Copying, renaming, and moving</h2>

  <p><code>FileEntry</code> and <code>DirectoryEntry</code> share common operations.</p>

  <h3 id="toc-dir-move-copy">Copying an entry</h3>

  <p>Both <code>FileEntry</code> and <code>DirectoryEntry</code> have a
  <code>copyTo()</code> for duplicating existing entries. This method automatically
  does a recursive copy on folders.</p>

  <p>The following code example copies the file "me.png" from one directory to another:</p>

  <pre class="prettyprint">
function copy(cwd, src, dest) {
  cwd.getFile(src, {}, function(fileEntry) {

    cwd.getDirectory(dest, {}, function(dirEntry) {
      <b>fileEntry.copyTo</b>(dirEntry);
    }, errorHandler);

  }, errorHandler);
}

window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
  copy(fs.root, '/folder1/me.png', 'folder2/mypics/');
}, errorHandler);
</pre>

  <h3 id="toc-dir-move-rename">Moving or renaming an entry</h3>

  <p>The <code>moveTo()</code> method present in
  <code>FileEntry</code> and <code>DirectoryEntry</code>
  allows you to move or rename a file or directory.
  Its first argument is the parent directory to move the file under, and its second
  is an optional new name for the file. If a new name isn't provided, the file's
  original name is used.</p>

  <p>The following example renames "me.png" to "you.png", but does not move the file:</p>

  <pre class="prettyprint">
function rename(cwd, src, newName) {
  cwd.getFile(src, {}, function(fileEntry) {
    <b>fileEntry.moveTo</b>(cwd, newName);
  }, errorHandler);
}

window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
  rename(fs.root, 'me.png', 'you.png');
}, errorHandler);
</pre>

  <p>The following example moves "me.png" (located in the root directory) to
  a folder named "newfolder".</p>

  <pre class="prettyprint">
function move(src, dirName) {
  fs.root.getFile(src, {}, function(fileEntry) {

    fs.root.getDirectory(dirName, {}, function(dirEntry) {
      <b>fileEntry.moveTo</b>(dirEntry);
    }, errorHandler);

  }, errorHandler);
}

window.requestFileSystem(window.TEMPORARY, 1024*1024, function(fs) {
  move('/me.png', 'newfolder/');
}, errorHandler);
</pre>

  <h2 id="toc-filesystemurls">filesystem: URLs</h2>

  <p>The FileSystem API exposes a new URL scheme, <code>filesystem:</code>,
  that can be used to fill <code>src</code> or <code>href</code> attributes.
  For example, if you wanted to display an image and have its
  <a href="http://dev.w3.org/2009/dap/file-system/pub/FileSystem/#the-fileentry-interface"><code>fileEntry</code></a>,
  calling <code>toURL()</code> would give you the file's <code>filesystem:</code> URL:</p>

<pre class="prettyprint">
var img = document.createElement('img');
img.src = <b>fileEntry.toURL</b>(); // filesystem:http://example.com/temporary/myfile.png
document.body.appendChild(img);
</pre>

  <p>Alternatively, if you already have a <code>filesystem:</code> URL,
  <code>resolveLocalFileSystemURL()</code> will get you back the
  <a href="http://dev.w3.org/2009/dap/file-system/pub/FileSystem/#the-fileentry-interface"><code>fileEntry</code></a>:</p>

<pre class="prettyprint">
window.resolveLocalFileSystemURL = window.resolveLocalFileSystemURL ||
                                   window.webkitResolveLocalFileSystemURL;

var url = 'filesystem:http://example.com/temporary/myfile.png';
<b>window.resolveLocalFileSystemURL</b>(url, function(fileEntry) {
  ...
});
</pre>

  <h2 id="toc-dir-fullexample">Putting it all together</h2>

  <h3 id="toc-samples-basic">Basic example</h3>
  <p>This demo lists the files/folders in the filesystem.</p>

  {% if is_mobile %}
  <div id="example-list-fs" class="example">
    <button>Add some files</button> <button>List files</button> <button>Remove all files</button>
    <ul id="example-list-fs-ul"></ul>
  </div>
  {% else %}
  <iframe src="http://playground.html5rocks.com/?mode=frame&hu=180&hl=160#filesystem_apis" style="border: none; width: 100%; height: 460px;"></iframe>
  {% endif %}

  <h3 id="toc-samples-terminal">HTML5 Terminal</h3>
  <p>This shell replicates some of the common operations in a UNIX filesystem
  (such as <code>cd</code>, <code>mkdir</code>, <code>rm</code>, <code>open</code>,
  and <code>cat</code>) by abstracting the FileSystem API. To add content, open
  the app, then drag and drop files from your desktop onto the terminal window.</p>

  <!--<iframe id="terminal-iframe" src="terminal.html"></iframe>-->

  <figure>
    <a href="http://www.htmlfivewow.com/demos/terminal/terminal.html"target="_blank">
      <img src="terminal.png" alt="Click to open the HTML5 Wow Terminal" title="Click to open the HTML5 Wow Terminal">
    </a>
    <figcaption>
      <a href="http://www.htmlfivewow.com/demos/terminal/terminal.html"target="_blank">Open the HTML5 Terminal</a>
    </figcaption>
  </figure>

  <h2 id="toc-usecases">Use Cases</h2>

  <p>There are several <a href="/tutorials#offline,storage">storage options</a>
  available in HTML5, but the FileSystem is different in that it aims to satisfy
  client-side storage use cases not well served by databases. Generally, these
  are applications that deal with large binary blobs and/or share data with
  applications outside of the context of the browser.</p>

  <p>The specification lists several use cases:</p>
  <ol>
    <li>Persistent uploader
      <ul>
      <li>When a file or directory is selected for upload, it copies the files
      into a local sandbox and uploads a chunk at a time.</li>
      <li>Uploads can be restarted after browser crashes, network interruptions, etc.</li>
      </ul>
    </li>
    <li>Video game, music, or other app with lots of media assets
      <ul>
      <li>It downloads one or several large tarballs, and expands them locally
      into a directory structure.</li>
      <li>The same download works on any operating system.</li>
      <li>It can manage prefetching just the next-to-be-needed assets in
      the background, so going to the next game level or activating a new
      feature doesn't require waiting for a download.</li>
      <li>It uses those assets directly from its local cache, by direct
      file reads or by handing local URIs to image or video tags, WebGL asset loaders, etc.</li>
      <li>The files may be of arbitrary binary format.</li>
      <li>On the server side, a compressed tarball will often be much
      smaller than a collection of separately-compressed files. Also, 1
      tarball instead of 1000 little files will involve fewer seeks, all
      else being equal.</li>
      </ul>
    </li>
    <li>Audio/Photo editor with offline access or local cache for speed
      <ul>
      <li>The data blobs are potentially quite large, and are read-write.</li>
      <li>It may want to do partial writes to files (ovewriting just the
      ID3/EXIF tags, for example).</li>
      <li>The ability to organize project files by creating directories would be useful.</li>
      <li>Edited files should be accessable by client-side applications [iTunes, Picasa].
      </li></ul>
    </li>
    <li>Offline video viewer
      <ul>
      <li>It downloads large files (&gt;1GB) for later viewing.</li>
      <li>It needs efficient seek + streaming.</li>
      <li>It must be able to hand a URI to the video tag.</li>
      <li>It should enable access to partly-downloaded files e.g. to let
      you watch the first episode of the DVD even if your download didn't
      complete before you got on the plane.</li>
      <li>It should be able to pull a single episode out of the middle of
      a download and give just that to the video tag.</li>
      </ul>
    </li>
    <li>Offline Web Mail Client
      <ul>
      <li>Downloads attachments and stores them locally.</li>
      <li>Caches user-selected attachments for later upload.</li>
      <li>Needs to be able to refer to cached attachments and image
      thumbnails for display and upload.</li>
      <li>Should be able to trigger the UA's download manager just as if
      talking to a server.</li>
      <li>Should be able to upload an email with attachments as a
      multipart post, rather than sending a file at a time in an XHR.</li>
      </ul>
    </li>
  </ol>

  <h2 id="toc-references">Reference specifications</h2>
  <ul>
    <li><a href="http://dev.w3.org/2009/dap/file-system/pub/FileSystem/">FileSystem</a></li>
    <li><a href="http://dev.w3.org/2009/dap/file-system/file-writer.html">FileWriter</a></li>
    <li><a href="http://dev.w3.org/2006/webapi/FileAPI/#dfn-filereader">FileReader</a></li>
    <li><a href="http://dev.w3.org/2006/webapi/FileAPI/">File</a></li>
    <li><a href="http://dev.w3.org/2006/webapi/FileAPI/#dfn-Blob">Blob</a></li>
  </ul>

{% if is_mobile %}
<script>
window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem;
var fs = null;

function errorHandler(e) {
  var msg = '';
  switch (e.code) {
    case FileError.QUOTA_EXCEEDED_ERR:
      msg = 'QUOTA_EXCEEDED_ERR';
      break;
    case FileError.NOT_FOUND_ERR:
      msg = 'NOT_FOUND_ERR';
      break;
    case FileError.SECURITY_ERR:
      msg = 'SECURITY_ERR';
      break;
    case FileError.INVALID_MODIFICATION_ERR:
      msg = 'INVALID_MODIFICATION_ERR';
      break;
    case FileError.INVALID_STATE_ERR:
      msg = 'INVALID_STATE_ERR';
      break;
    default:
      msg = 'Unknown Error';
      break;
  };
  document.querySelector('#example-list-fs-ul').innerHTML = 'Error: ' + msg;
}

function initFS() {
  window.requestFileSystem(window.TEMPORARY, 1024*1024, function(filesystem) {
    fs = filesystem;
  }, errorHandler);
}

// Example - Reader Dir Contents.
(function() {
  var buttons = document.querySelectorAll('#example-list-fs button');
  var filelist = document.querySelector('#example-list-fs-ul');

  buttons[0].addEventListener('click', function(e) {
    if (!fs) {
      return;
    }
    fs.root.getFile('log.txt', {create: true}, null, errorHandler);
    fs.root.getFile('song.mp3', {create: true}, null, errorHandler);
    fs.root.getDirectory('mypictures', {create: true}, null, errorHandler);
    filelist.innerHTML = 'Files created.';
  }, false);

  buttons[1].addEventListener('click', function(e) {
    if (!fs) {
      return;
    }

    var dirReader = fs.root.createReader();
    dirReader.readEntries(function(entries) {
      if (!entries.length) {
        filelist.innerHTML = 'Filesystem is empty.';
      } else {
        filelist.innerHTML = '';
      }

      var fragment = document.createDocumentFragment();
      for (var i = 0, entry; entry = entries[i]; ++i) {
        var img = entry.isDirectory ? '<img src="/static/images/tutorials/icon-folder.gif">' :
                                      '<img src="/static/images/tutorials/icon-file.gif">';
        var li = document.createElement('li');
        li.innerHTML = [img, '<span>', entry.name, '</span>'].join('');
        fragment.appendChild(li);
      }
      filelist.appendChild(fragment);
    }, errorHandler);
  }, false);

  buttons[2].addEventListener('click', function(e) {
    if (!fs) {
      return;
    }

    var dirReader = fs.root.createReader();
    dirReader.readEntries(function(entries) {
      for (var i = 0, entry; entry = entries[i]; ++i) {
        if (entry.isDirectory) {
          entry.removeRecursively(function() {}, errorHandler);
        } else {
          entry.remove(function() {}, errorHandler);
        }
      }
      filelist.innerHTML = 'Directory emptied.';
    }, errorHandler);
  }, false);
})();

if (window.requestFileSystem) {
  initFS();  // Initiate filesystem on page load.
}
</script>
{% endif %}

{% endblock %}
